#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>

#define GLOBALMEM_SIZE 0x1000
#define GLBALMEM_MAGIC 'g'
#define MEM_CLEAR    _IO(GLBALMEM_MAGIC,0)
#define GLOBALMEM_MAJOR 230

static int globalmem_major = GLOBALMEM_MAJOR;
module_param(globalmem_major,int,S_IRUGO);

struct globalmem_dev{
	struct cdev cdev;
	unsigned char mem[GLOBALMEM_SIZE];
};

struct globalmem_dev *globalmem_devp;

static ssize_t  globalmem_read(struct file *filep, char __user * buf, size_t size, loff_t * ppos)
{
	unsigned long p = *ppos;
	unsigned int count = size;
	int ret = 0;

	struct globalmem_dev *dev = filep->private_data;

	if(p>GLOBALMEM_SIZE)
	{	
		return 0;
	}
	if(count >GLOBALMEM_SIZE -p)
		count = GLOBALMEM_SIZE -p;
	if(copy_to_user(buf,dev->mem+p,count))
	{
		ret = -EFAULT;
	}
	else{
		*ppos +=count;
		ret = count;
		printk(  "read %u bytes(s) form %1u \n",count,(unsigned int)p);
	}
	return ret;
}
static ssize_t   globalmem_write(struct file *filep, const char __user * buf, size_t size, loff_t *ppos)  
{
	unsigned long p = *ppos;
	unsigned int count = size;
	int ret = 0;

	struct globalmem_dev *dev = filep->private_data;

	if(p>GLOBALMEM_SIZE)
	{	
		return 0;
	}
	if(count >GLOBALMEM_SIZE -p)
		count = GLOBALMEM_SIZE -p;
	if(copy_from_user(dev->mem+p,buf,count)){
		ret = -EFAULT;
	}	
	else{
		*ppos = count;
		ret = count;
		printk( "write %u bytes(s) form %1u \n",count,(unsigned int)p);
	}
	return ret;
}
static long   globalmem_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
{
	struct globalmem_dev *dev = filep->private_data;
	switch(cmd){
		case MEM_CLEAR:
			memset(dev->mem,0,GLOBALMEM_SIZE);
			printk( "globalmem is set to zero \n");
			break;
		default:
			return -EFAULT;
	}
	return 0;
}
static int globalmem_open(struct inode *innode,struct file* filep)
{

	filep->private_data = globalmem_devp;
//	memset(filep->private_data->mem,0,GLOBALMEM_SIZE);
	return 0;
}
static int globalmem_release(struct inode *inode,struct file* filep)
{
	return 0;
}
static const struct file_operations globalmem_fops={
	.owner  = THIS_MODULE,
//	.llseek = globalmem_llseek,
	.read   = globalmem_read,
	.write  = globalmem_write,
	.unlocked_ioctl = globalmem_ioctl,
	.open   = globalmem_open,
	.release = globalmem_release,
};

static void globalmem_setup_cdev(struct globalmem_dev *dev,int index )
{
	int err , devno = MKDEV(globalmem_major,index);
	cdev_init(&dev->cdev,&globalmem_fops);
	dev->cdev.owner = THIS_MODULE;
	err = cdev_add(&dev->cdev,devno,1);
	if(err){
		printk(KERN_NOTICE"error %d adding globalmem %d",err,index);
	}
}


static int __init globalmem_module_init(void)
{
	int ret;
	dev_t devno ;
	devno= MKDEV(globalmem_major,0);
	if(globalmem_major){
		ret = register_chrdev_region(devno,1,"globalmem");
	}
	else{
		ret = alloc_chrdev_region(&devno,0,1,"globalmem");
	}
	if(ret<0)
	{
		return ret;
	}

	globalmem_devp = kzalloc(sizeof(struct globalmem_dev),GFP_KERNEL);
	if(!globalmem_devp){
		ret = -ENOMEM;
		goto fail_malloc;
	}

	globalmem_setup_cdev(globalmem_devp,0);
	return 0;
fail_malloc:
	unregister_chrdev_region(devno,1);
	return ret;
}



static void __exit globalmem_module_exit(void)
{
	cdev_del(&globalmem_devp->cdev);
	kfree(globalmem_devp);
	unregister_chrdev_region(MKDEV(globalmem_major,0),1);
	printk( "bye bye !\n");
}
module_init(globalmem_module_init);
module_exit(globalmem_module_exit);

MODULE_AUTHOR("zhengchunyu");
MODULE_LICENSE("GPL v2");

